home *** CD-ROM | disk | FTP | other *** search
/ Total Network Tools 2002 / NextStepPublishing-TotalNetworkTools2002-Win95.iso / Archive / Misc Servers / Zope.exe / BASEREQUEST.PY < prev    next >
Encoding:
Python Source  |  2000-11-01  |  19.3 KB  |  537 lines

  1. ##############################################################################
  2. # Zope Public License (ZPL) Version 1.0
  3. # -------------------------------------
  4. # Copyright (c) Digital Creations.  All rights reserved.
  5. # This license has been certified as Open Source(tm).
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions are
  8. # met:
  9. # 1. Redistributions in source code must retain the above copyright
  10. #    notice, this list of conditions, and the following disclaimer.
  11. # 2. Redistributions in binary form must reproduce the above copyright
  12. #    notice, this list of conditions, and the following disclaimer in
  13. #    the documentation and/or other materials provided with the
  14. #    distribution.
  15. # 3. Digital Creations requests that attribution be given to Zope
  16. #    in any manner possible. Zope includes a "Powered by Zope"
  17. #    button that is installed by default. While it is not a license
  18. #    violation to remove this button, it is requested that the
  19. #    attribution remain. A significant investment has been put
  20. #    into Zope, and this effort will continue if the Zope community
  21. #    continues to grow. This is one way to assure that growth.
  22. # 4. All advertising materials and documentation mentioning
  23. #    features derived from or use of this software must display
  24. #    the following acknowledgement:
  25. #      "This product includes software developed by Digital Creations
  26. #      for use in the Z Object Publishing Environment
  27. #      (http://www.zope.org/)."
  28. #    In the event that the product being advertised includes an
  29. #    intact Zope distribution (with copyright and license included)
  30. #    then this clause is waived.
  31. # 5. Names associated with Zope or Digital Creations must not be used to
  32. #    endorse or promote products derived from this software without
  33. #    prior written permission from Digital Creations.
  34. # 6. Modified redistributions of any form whatsoever must retain
  35. #    the following acknowledgment:
  36. #      "This product includes software developed by Digital Creations
  37. #      for use in the Z Object Publishing Environment
  38. #      (http://www.zope.org/)."
  39. #    Intact (re-)distributions of any official Zope release do not
  40. #    require an external acknowledgement.
  41. # 7. Modifications are encouraged but must be packaged separately as
  42. #    patches to official Zope releases.  Distributions that do not
  43. #    clearly separate the patches from the original work must be clearly
  44. #    labeled as unofficial distributions.  Modifications which do not
  45. #    carry the name Zope may be packaged in any form, as long as they
  46. #    conform to all of the clauses above.
  47. # Disclaimer
  48. #   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
  49. #   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  50. #   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  51. #   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
  52. #   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  53. #   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  54. #   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  55. #   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  56. #   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  57. #   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  58. #   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  59. #   SUCH DAMAGE.
  60. # This software consists of contributions made by Digital Creations and
  61. # many individuals on behalf of Digital Creations.  Specific
  62. # attributions are listed in the accompanying credits file.
  63. ##############################################################################
  64. __version__='$Revision: 1.27.2.4 $'[11:-2]
  65.  
  66. from string import join, split, find, rfind, lower, upper
  67. from urllib import quote
  68.  
  69. UNSPECIFIED_ROLES=''
  70.  
  71. try:
  72.     from ExtensionClass import Base
  73.     class RequestContainer(Base):
  74.         __roles__=None
  75.         def __init__(self,**kw):
  76.             for k,v in kw.items(): self.__dict__[k]=v
  77.  
  78.         def manage_property_types(self):
  79.             return type_converters.keys()
  80.             
  81. except:
  82.     class RequestContainer:
  83.         __roles__=None
  84.         def __init__(self,**kw):
  85.             for k,v in kw.items(): self.__dict__[k]=v
  86.  
  87. _marker=[]
  88. class BaseRequest:
  89.     """Provide basic ZPublisher request management
  90.     
  91.     This object provides access to request data. Request data may
  92.     vary depending on the protocol used.
  93.  
  94.     Request objects are created by the object publisher and will be
  95.     passed to published objects through the argument name, REQUEST.
  96.  
  97.     The request object is a mapping object that represents a
  98.     collection of variable to value mappings.
  99.     """
  100.  
  101.     maybe_webdav_client = 1
  102.  
  103.     # While the following assignment is not strictly necessary, it
  104.     # prevents alot of unnecessary searches because, without it,
  105.     # acquisition of REQUEST is disallowed, which penalizes access
  106.     # in DTML with tags.
  107.     __roles__ = None
  108.     _file=None
  109.     common={} # Common request data
  110.     _auth=None
  111.     _held=()
  112.  
  113.     # Allow (reluctantly) access to unprotected attributes
  114.     __allow_access_to_unprotected_subobjects__=1
  115.         
  116.     def __init__(self, other=None, **kw):
  117.         """The constructor is not allowed to raise errors
  118.         """
  119.         if other is None: other=kw
  120.         else: other.update(kw)
  121.         self.other=other
  122.  
  123.     def close(self):
  124.         self.other.clear()
  125.         self._held=None
  126.  
  127.     def processInputs(self):
  128.         """Do any input processing that could raise errors
  129.         """
  130.  
  131.     def __len__(self):
  132.         return 1
  133.  
  134.     def __setitem__(self,key,value):
  135.         """Set application variables
  136.  
  137.         This method is used to set a variable in the requests "other"
  138.         category.
  139.         """
  140.         self.other[key]=value
  141.  
  142.     set=__setitem__
  143.  
  144.     def __getitem__(self,key,
  145.                     default=_marker, # Any special internal marker will do
  146.                     ):
  147.         """Get a variable value
  148.  
  149.         Return a value for the required variable name.
  150.         The value will be looked up from one of the request data
  151.         categories. The search order is environment variables,
  152.         other variables, form data, and then cookies. 
  153.         
  154.         """
  155.         if key=='REQUEST': return self
  156.  
  157.         v=self.other.get(key, _marker)
  158.         if v is not _marker: return v
  159.         v=self.common.get(key, default)
  160.         if v is not _marker: return v
  161.  
  162.         if key=='BODY' and self._file is not None:
  163.             p=self._file.tell()
  164.             self._file.seek(0)
  165.             v=self._file.read()
  166.             self._file.seek(p)
  167.             self.other[key]=v
  168.             return v
  169.  
  170.         if key=='BODYFILE' and self._file is not None:
  171.             v=self._file
  172.             self.other[key]=v
  173.             return v
  174.         
  175.         raise KeyError, key
  176.  
  177.     __getattr__=get=__getitem__
  178.  
  179.     def has_key(self,key):
  180.         return self.get(key, _marker) is not _marker
  181.  
  182.     def keys(self):
  183.         keys = {}
  184.         keys.update(self.common)
  185.         keys.update(self.other)
  186.         return keys.keys()
  187.  
  188.     def items(self):
  189.         result = []
  190.         get=self.get
  191.         for k in self.keys():
  192.             result.append((k, get(k)))
  193.         return result
  194.  
  195.     def values(self):
  196.         result = []
  197.         get=self.get
  198.         for k in self.keys():
  199.             result.append(get(k))
  200.         return result
  201.  
  202.     def __str__(self):
  203.         L1 = self.items()
  204.         L1.sort()
  205.         return join(map(lambda item: "%s:\t%s" % item, L1), "\n")
  206.  
  207.     __repr__=__str__
  208.  
  209.  
  210.     def traverse(self, path, response=None, validated_hook=None):
  211.         """Traverse the object space
  212.  
  213.         The REQUEST must already have a PARENTS item with at least one
  214.         object in it.  This is typically the root object.
  215.         """
  216.         request=self
  217.         request_get=request.get
  218.         if response is None: response=self.response
  219.         debug_mode=response.debug_mode
  220.  
  221.         # Cleanup the path list
  222.         if path[:1]=='/':  path=path[1:]
  223.         if path[-1:]=='/': path=path[:-1]
  224.         clean=[]
  225.         for item in split(path, '/'):
  226.             # Make sure that REQUEST cannot be traversed.
  227.             if item == 'REQUEST':
  228.                 return response.notFoundError(path)            
  229.             if not item or item=='.':
  230.                 continue
  231.             elif item == '..':
  232.                 del clean[-1]
  233.             else: clean.append(item)
  234.         path=clean
  235.     
  236.         # How did this request come in? (HTTP GET, PUT, POST, etc.)
  237.         method=req_method=upper(request_get('REQUEST_METHOD', 'GET'))
  238.         
  239.         no_acquire_flag=0
  240.  
  241.         # Set the default method
  242.         if method=='GET' or method=='POST':
  243.             method='index_html'
  244.         else:
  245.             if self.maybe_webdav_client:
  246.                 # Probably a WebDAV client.
  247.                 no_acquire_flag=1
  248.         URL=request['URL']
  249.         
  250.         parents=request['PARENTS']
  251.         object=parents[-1]
  252.         del parents[:]
  253.  
  254.         roles = getattr(object, '__roles__', UNSPECIFIED_ROLES)
  255.         
  256.         # if the top object has a __bobo_traverse__ method, then use it
  257.         # to possibly traverse to an alternate top-level object.
  258.         if hasattr(object,'__bobo_traverse__'):
  259.             try:
  260.                 object=object.__bobo_traverse__(request)
  261.                 roles =getattr(object, '__roles__', UNSPECIFIED_ROLES)
  262.             except: pass            
  263.  
  264.         if not path and not method:
  265.             return response.forbiddenError(self['URL'])
  266.  
  267.         # Traverse the URL to find the object:
  268.         if hasattr(object, '__of__'): 
  269.             # Try to bind the top-level object to the request
  270.             # This is how you get 'self.REQUEST'
  271.             object=object.__of__(RequestContainer(REQUEST=request))
  272.         parents.append(object)
  273.  
  274.         steps=self.steps
  275.         self._steps = _steps = map(quote, steps)
  276.         path.reverse()
  277.         pop = path.pop
  278.         request['TraversalRequestNameStack'] = request.path = path
  279.  
  280.         entry_name = ''
  281.         try:
  282.             # We build parents in the wrong order, so we
  283.             # need to make sure we reverse it when we're doe.
  284.             while 1:
  285.                 bpth = getattr(object, '__before_publishing_traverse__', None)
  286.                 if bpth is not None:
  287.                     bpth(object, self)
  288.                 # Check for method:
  289.                 if path:
  290.                     entry_name = pop()
  291.                 elif (method and hasattr(object,method)
  292.                       and entry_name != method
  293.                       and getattr(object, method) is not None):
  294.                     request._hacked_path=1
  295.                     entry_name = method
  296.                 else:
  297.                     if (hasattr(object, '__call__') and
  298.                         hasattr(object.__call__,'__roles__')):
  299.                         roles=object.__call__.__roles__
  300.                     if request._hacked_path:
  301.                         i=rfind(URL,'/')
  302.                         if i > 0: response.setBase(URL[:i])
  303.                     break
  304.                 if not entry_name: continue
  305.                 step = quote(entry_name)
  306.                 _steps.append(step)
  307.                 request['URL'] = URL = '%s/%s' % (request['URL'], step)
  308.                 got = 0
  309.                 if entry_name[:1]=='_':
  310.                     if debug_mode:
  311.                         return response.debugError(
  312.                           "Object name begins with an underscore at: %s" % URL)
  313.                     else: return response.forbiddenError(entry_name)
  314.     
  315.                 if hasattr(object,'__bobo_traverse__'):
  316.                     subobject=object.__bobo_traverse__(request,entry_name)
  317.                     if type(subobject) is type(()) and len(subobject) > 1:
  318.                         # Add additional parents into the path
  319.                         parents[-1:] = subobject[:-1]
  320.                         object, subobject = subobject[-2:]
  321.                 else:
  322.                     try:
  323.  
  324.                         # Note - no_acquire_flag is necessary to support
  325.                         # things like DAV.  We have to make sure
  326.                         # that the target object is not acquired
  327.                         # if the request_method is other than GET
  328.                         # or POST. Otherwise, you could never use
  329.                         # PUT to add a new object named 'test' if
  330.                         # an object 'test' existed above it in the
  331.                         # heirarchy -- you'd always get the
  332.                         # existing object :(
  333.                         
  334.                         if no_acquire_flag and hasattr(object, 'aq_base'):
  335.                             if hasattr(object.aq_base, entry_name):
  336.                                 subobject=getattr(object, entry_name)
  337.                             else: raise AttributeError, entry_name
  338.                         else: subobject=getattr(object, entry_name)
  339.                     except AttributeError:
  340.                         got=1
  341.                         try: subobject=object[entry_name]
  342.                         except (KeyError, IndexError,
  343.                                 TypeError, AttributeError):
  344.                             if debug_mode:
  345.                                 return response.debugError(
  346.                                     "Cannot locate object at: %s" % URL) 
  347.                             else:
  348.                                 return response.notFoundError(URL)
  349.     
  350.                 try:
  351.                     try: doc=subobject.__doc__
  352.                     except: doc=getattr(object, entry_name+'__doc__')
  353.                     if not doc: raise AttributeError, entry_name
  354.                 except:
  355.                     if debug_mode:
  356.                         return response.debugError(
  357.                             "Missing doc string at: %s" % URL)
  358.                     else: return response.notFoundError("%s" % URL)
  359.  
  360.                 r = getattr(subobject, '__roles__', UNSPECIFIED_ROLES)
  361.                 if r is not UNSPECIFIED_ROLES:
  362.                     roles = r
  363.                 elif not got:
  364.                     roles = getattr(subobject, entry_name+'__roles__', roles)
  365.  
  366.                 # Promote subobject to object
  367.                 object=subobject
  368.                 parents.append(object)
  369.  
  370.                 steps.append(entry_name)
  371.         finally:
  372.             parents.reverse()
  373.  
  374.         popped_last = 0
  375. #        parents.pop(0) # Get rid of final method object
  376.  
  377.         # Do authorization checks
  378.         user=groups=None
  379.         i=0
  380.         try:
  381.             if roles is not None:
  382.  
  383.                 last_parent_index=len(parents)
  384.                 if hasattr(object, '__allow_groups__'):
  385.                     groups=object.__allow_groups__
  386.                     inext=0
  387.                 else:
  388.                     inext=None
  389.                     for i in range(last_parent_index):
  390.                         if hasattr(parents[i],'__allow_groups__'):
  391.                             groups=parents[i].__allow_groups__
  392.                             inext=i+1
  393.                             break
  394.  
  395.                 if inext is not None:
  396.                     i=inext
  397.  
  398.                     if hasattr(groups, 'validate'): v=groups.validate
  399.                     else: v=old_validation
  400.  
  401.                     auth=request._auth
  402.  
  403.                     if v is old_validation and roles is UNSPECIFIED_ROLES:
  404.                         # No roles, so if we have a named group, get roles from
  405.                         # group keys
  406.                         if hasattr(groups,'keys'): roles=groups.keys()
  407.                         else:
  408.                             try: groups=groups()
  409.                             except: pass
  410.                             try: roles=groups.keys()
  411.                             except: pass
  412.  
  413.                         if groups is None:
  414.                             # Public group, hack structures to get it to validate
  415.                             roles=None
  416.                             auth=''
  417.  
  418.                     if v is old_validation:
  419.                         user=old_validation(groups, request, auth, roles)
  420.                     elif roles is UNSPECIFIED_ROLES: user=v(request, auth)
  421.                     else: user=v(request, auth, roles)
  422.  
  423.                     while user is None and i < last_parent_index:
  424.                         parent=parents[i]
  425.                         i=i+1
  426.                         if hasattr(parent, '__allow_groups__'): 
  427.                             groups=parent.__allow_groups__
  428.                         else: continue
  429.                         if hasattr(groups,'validate'): v=groups.validate
  430.                         else: v=old_validation
  431.                         if v is old_validation:
  432.                             user=old_validation(groups, request, auth, roles)
  433.                         elif roles is UNSPECIFIED_ROLES: user=v(request, auth)
  434.                         else: user=v(request, auth, roles)
  435.  
  436.                 if not popped_last:
  437.                     # Get rid of final method object
  438.                     parents.pop(0)
  439.                     popped_last=1
  440.  
  441.                 if user is None and roles != UNSPECIFIED_ROLES:
  442.                     response.unauthorized()
  443.  
  444.         finally:
  445.             # Get rid of final method object
  446.             if not popped_last:
  447.                 parents.pop(0)
  448.  
  449.         if user is not None:
  450.             if validated_hook is not None: validated_hook(self, user)
  451.             request['AUTHENTICATED_USER']=user
  452.             request['AUTHENTICATION_PATH']=join(steps[:-i],'/')
  453.  
  454.         # Remove http request method from the URL.
  455.         request['URL']=URL
  456.     
  457.         return object
  458.  
  459.     retry_count=0
  460.     def supports_retry(self): return 0
  461.  
  462.     def _hold(self, object):
  463.         """Hold a reference to an object to delay it's destruction until mine
  464.         """
  465.         self._held=self._held+(object,)
  466.  
  467.  
  468.  
  469.  
  470. def old_validation(groups, request, auth,
  471.                    roles=UNSPECIFIED_ROLES):
  472.  
  473.     if auth:
  474.         auth=request._authUserPW()
  475.         if auth: name,password = auth
  476.         elif roles is None: return ''
  477.         else: return None
  478.     elif request.environ.has_key('REMOTE_USER'):
  479.         name=request.environ['REMOTE_USER']
  480.         password=None
  481.     else:
  482.         if roles is None: return ''
  483.         return None
  484.  
  485.     if roles is None: return name
  486.  
  487.     keys=None
  488.     try:
  489.         keys=groups.keys
  490.     except:
  491.         try:
  492.             groups=groups() # Maybe it was a method defining a group
  493.             keys=groups.keys
  494.         except: pass
  495.  
  496.     if keys is not None:
  497.         # OK, we have a named group, so apply the roles to the named
  498.         # group.
  499.         if roles is UNSPECIFIED_ROLES: roles=keys()
  500.         g=[]
  501.         for role in roles:
  502.             if groups.has_key(role): g.append(groups[role])
  503.         groups=g
  504.  
  505.     for d in groups:
  506.         if d.has_key(name) and (d[name]==password or password is None):
  507.             return name
  508.  
  509.     if keys is None:
  510.         # Not a named group, so don't go further
  511.         raise 'Forbidden', (
  512.             """<strong>You are not authorized to access this resource""")
  513.  
  514.     return None
  515.  
  516.